#include "user_repository.h"
#include <spdlog/spdlog.h>

int UserRepository::create(User& user) {
    PreparedStatement stmt(db_, "INSERT INTO users (username, password, created_at, updated_at) VALUES (?, ?, ?, ?)");
    
    // Check statement preparation by trying to bind
    DbError error = stmt.bind_text(1, user.username);
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to prepare statement or bind username: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    error = stmt.bind_text(2, user.password);
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to bind password: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    error = stmt.bind_text(3, user.created_at);
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to bind created_at: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    error = stmt.bind_text(4, user.updated_at);
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to bind updated_at: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    // Execute statement
    error = stmt.step();
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to execute insert statement: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    // Set the user ID from the last inserted row ID
    user.id = static_cast<int>(stmt.last_insert_rowid());

    return REPO_SUCCESS;
}

int UserRepository::update(const User& user) {
    PreparedStatement stmt(db_, "UPDATE users SET username = ?, password = ?, updated_at = ? WHERE id = ?");
    
    // Check statement preparation by trying to bind
    DbError error = stmt.bind_text(1, user.username);
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to prepare statement or bind username: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    error = stmt.bind_text(2, user.password);
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to bind password: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    error = stmt.bind_text(3, user.updated_at);
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to bind updated_at: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    error = stmt.bind_int(4, user.id);
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to bind id: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    // Execute statement
    error = stmt.step();
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to execute update statement: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    // Check if any rows were affected
    if (stmt.changes() == 0) {
        return REPO_ERROR_DATABASE;  // No rows affected, treat as error
    }

    return REPO_SUCCESS;
}

int UserRepository::remove(int id) {
    PreparedStatement stmt(db_, "DELETE FROM users WHERE id = ?");
    
    // Check statement preparation by trying to bind
    DbError error = stmt.bind_int(1, id);
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to prepare statement or bind id: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    // Execute statement
    error = stmt.step();
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to execute delete statement: {}", static_cast<int>(error));
        return REPO_ERROR_DATABASE;
    }

    // Check if any rows were affected
    if (stmt.changes() == 0) {
        return REPO_ERROR_DATABASE;  // No rows affected, treat as error
    }

    return REPO_SUCCESS;
}

std::optional<User> UserRepository::findById(int id) {
    PreparedStatement stmt(db_, "SELECT id, username, password, created_at, updated_at FROM users WHERE id = ?");
    
    // Check statement preparation by trying to bind
    DbError error = stmt.bind_int(1, id);
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to prepare statement or bind id: {}", static_cast<int>(error));
        return std::nullopt;
    }

    // Execute statement
    error = stmt.step();
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to execute select statement: {}", static_cast<int>(error));
        return std::nullopt;
    }

    // Check if we have a row
    if (!stmt.has_row()) {
        return std::nullopt;
    }

    // Create user from result
    User user;
    user.id = stmt.column_int(0);
    user.username = std::string(stmt.column_text(1));
    user.password = std::string(stmt.column_text(2));
    user.created_at = std::string(stmt.column_text(3));
    user.updated_at = std::string(stmt.column_text(4));

    return user;
}

std::vector<User> UserRepository::findAll() {
    PreparedStatement stmt(db_, "SELECT id, username, password, created_at, updated_at FROM users");
    
    // For SELECT statements without parameters, we can't check preparation with bind
    // Let's just try to execute and see if it works
    std::vector<User> users;
    
    // Execute statement and iterate through results
    DbError error = stmt.step();
    while (error == DbError::SUCCESS && stmt.has_row()) {
        User user;
        user.id = stmt.column_int(0);
        user.username = std::string(stmt.column_text(1));
        user.password = std::string(stmt.column_text(2));
        user.created_at = std::string(stmt.column_text(3));
        user.updated_at = std::string(stmt.column_text(4));
        
        users.push_back(user);
        
        // Move to next row
        error = stmt.step();
    }
    
    if (error != DbError::SUCCESS && error != DbError::DATABASE_ERROR) { // DATABASE_ERROR might be returned when no more rows
        spdlog::error("Failed to execute select all statement: {}", static_cast<int>(error));
    }

    return users;
}

std::optional<User> UserRepository::findByUsername(const std::string& username) {
    PreparedStatement stmt(db_, "SELECT id, username, password, created_at, updated_at FROM users WHERE username = ?");
    
    // Check statement preparation by trying to bind
    DbError error = stmt.bind_text(1, username);
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to prepare statement or bind username: {}", static_cast<int>(error));
        return std::nullopt;
    }

    // Execute statement
    error = stmt.step();
    if (error != DbError::SUCCESS) {
        spdlog::error("Failed to execute select by username statement: {}", static_cast<int>(error));
        return std::nullopt;
    }

    // Check if we have a row
    if (!stmt.has_row()) {
        return std::nullopt;
    }

    // Create user from result
    User user;
    user.id = stmt.column_int(0);
    user.username = std::string(stmt.column_text(1));
    user.password = std::string(stmt.column_text(2));
    user.created_at = std::string(stmt.column_text(3));
    user.updated_at = std::string(stmt.column_text(4));

    return user;
}